/*
 * wrapconnection.cxx
 *
 * OpenH323 Wrapper Library
 *
 * Copyright (c) 2002-2005 InAccess Networks
 * Michalis Manousos <manousos@inaccessnetworks.com>
 * Dimitris Economou <decon@inaccessnetworks.com>
 *
 * This file is part of "H.323 support for ASTERISK"
 *
 * "H.323 support for ASTERISK" is free software;
 * you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation;
 * either version 2 of the License, or (at your option) any later version. 
 *
 * "H.323 support for ASTERISK" is distributed in the hope that it will be 
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details. 
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 * $Id: wrapconnection.cxx,v 1.1.1.1 2007/06/04 10:55:32 mmanousos Exp $
 *
 */

/************************************************************************/

#include <ptlib.h>
#include <h323.h>
#include <h323pdu.h>
#include <mediafmt.h>
#include <lid.h>

#define cplusplus
#include "version.h"
#include "wrapper_misc.hxx"
#include "wrapper.hxx"
#include "wrapendpoint.hxx"
#include "wrapconnection.hxx"
#include "asteriskaudio.hxx"

using namespace std;

// External stuff used inside the wrapper library
extern start_logchan_cb		on_start_logical_channel; 
extern clear_con_cb			on_connection_cleared;
extern alert_con_cb			on_connection_alert;
extern h323_exception_cb	on_h323_exception;
extern init_con_cb			on_connection_init;

extern H323Capability *h323_capability_create(WrapH323EndPoint *ep, cap_type_t capability, 
				int frame_num);

/********************************************
 * Class WrapH323Connection
 ********************************************/

/*****************************************************************************
 * Class constructor
 */
WrapH323Connection::WrapH323Connection(WrapH323EndPoint & ep,
				   unsigned callReference, void *data)
: H323Connection(ep, callReference)
{
	user_details_t *ud = (user_details_t *)data;
	H323Capabilities ourCapabilities;

	//cout << localCapabilities << endl;

	if (ud != NULL) {
		WRAPTRACE(2, "Creation of WrapH323Connection based on user data.");
		userData = (user_details_t *)malloc(sizeof(user_details_t));
		if (userData == NULL) {
			WRAPTRACE(1, "Memory allocation failed.");
			return;
		}
		memset((char *)userData, 0, sizeof(user_details_t));
		*userData = *ud;
		if (ud->incoming_call) {
			WRAPTRACE(2, "Call is incoming.");
		} else {
			WRAPTRACE(2, "Call is outgoing.");
			if ((strlen(ud->username) > 0) || (strlen(ud->calling_num) > 0)) {
				localAliasNames.RemoveAll();
				if (strlen(ud->username) > 0) {
					localAliasNames.AppendString(ud->username);
				}
				if (strlen(ud->calling_num) > 0) {
					localAliasNames.AppendString(ud->calling_num);
				}
			}
		}
		if (ud->faststart > -1)
			fastStartState = ud->faststart ? FastStartInitiate : FastStartDisabled;
		if (ud->h245tunnel > -1)
			h245Tunneling = ud->h245tunnel ? TRUE : FALSE;
		if (ud->h245insetup > -1)
			doH245inSETUP = ud->h245insetup ? TRUE : FALSE;
		if (ud->prefcodec > -1) {
			/* Rebuild capabilities list */
			H323Capability *cap;
			cap = h323_capability_create(&ep, (cap_type_t)ud->prefcodec, 0);
			localCapabilities.RemoveAll();
			localCapabilities.SetCapability(0, 0, cap);
			SetSendUserInputMode(ep.GetSendUserInputMode());
			H323_UserInputCapability::AddAllCapabilities(localCapabilities,
							0, P_MAX_INDEX);
		}
	} else {
		WRAPTRACE(2, "Creation of WrapH323Connection based on default endpoint settings.");
		userData = NULL;
	}
	q931_cause = Q931::ErrorInCauseIE;

	//cout << localCapabilities << endl;

	WRAPTRACE(4, "WrapH323Connection created.");
	return;
}

/*****************************************************************************
 * Class destructor
 */
WrapH323Connection::~WrapH323Connection()
{
	if (userData)
		free(userData);
	WRAPTRACE(4, "WrapH323Connection deleted.");
	return;
}

/*****************************************************************************
 *
 */
unsigned int
WrapH323Connection::GetAppID()
{
	if (userData)
		return(userData->app_id);
	return(0);
}

/*****************************************************************************
 *
 */
void
WrapH323Connection::SetQ931Cause(int cause)
{
	q931_cause = (Q931::CauseValues)cause;
}

/*****************************************************************************
 *
 */
H323Connection::AnswerCallResponse
WrapH323Connection::OnAnswerCall(const PString & caller,
					const H323SignalPDU & setupPDU,
					H323SignalPDU & /*connectPDU*/)
{
	call_details_t cdet;
	int res = -1;
	PString tmp_str;
	PIPSocket::Address plocal, premote;
	PString sourceE164, destE164, rdnis;

	WRAPTRACE(2, "User " << caller << " is calling us...");

	if (Lock()) {
		// Fill the structure
		memset((char*)&cdet, 0, sizeof(cdet));
		strncpy(cdet.call_id, (const char*)GetCallIdentifier().AsString(), 
						sizeof(cdet.call_id) - 1);
		strncpy(cdet.conf_id, (const char*)GetConferenceIdentifier().AsString(), 
						sizeof(cdet.call_id) - 1);
		cdet.app_id = GetAppID();
		cdet.call_reference = GetCallReference();
		strncpy(cdet.call_token, (const char*)GetCallToken(), 
						sizeof(cdet.call_token) - 1);
		strncpy(cdet.call_source_alias, (const char *)setupPDU.GetSourceAliases(), 
						sizeof(cdet.call_source_alias) - 1);
		strncpy(cdet.call_dest_alias, (const char *)setupPDU.GetDestinationAlias(),
						sizeof(cdet.call_dest_alias) - 1);
		if (setupPDU.GetSourceE164(sourceE164)) {
			strncpy(cdet.call_source_e164, (const char *)sourceE164,
						sizeof(cdet.call_source_e164) - 1);
		}
		if (setupPDU.GetDestinationE164(destE164)) {
			strncpy(cdet.call_dest_e164, (const char *)destE164,
						sizeof(cdet.call_dest_e164) - 1);
		}
		if (setupPDU.GetQ931().GetRedirectingNumber(rdnis)) {
			strncpy(cdet.call_rdnis, (const char *)rdnis,
						sizeof(cdet.call_rdnis) - 1);
		}
		strncpy(cdet.remote_app, (const char *)GetRemoteApplication(),
						sizeof(cdet.remote_app) - 1);
		if (GetSignallingChannel() != NULL) {
			(GetSignallingChannel()->GetRemoteAddress()).GetIpAddress(premote);
			(GetSignallingChannel()->GetLocalAddress()).GetIpAddress(plocal);
			snprintf(cdet.local_addr, sizeof(cdet.local_addr)-1, 
						"%s", (const char *)plocal.AsString());
			snprintf(cdet.remote_addr, sizeof(cdet.remote_addr)-1, 
						"%s", (const char *)premote.AsString());
		}

		// DEBUG START
		WRAPTRACE(3, "Call ID: " << cdet.call_id);
		WRAPTRACE(3, "Conference ID: " << cdet.conf_id);
		WRAPTRACE(3, "Call reference: " << cdet.call_reference);
		WRAPTRACE(3, "Call token: " << cdet.call_token);
		WRAPTRACE(3, "Call source alias: "<< cdet.call_source_alias<<"("<<strlen(cdet.call_source_alias)<<")");
		WRAPTRACE(3, "Call dest alias: "<< cdet.call_dest_alias<<"("<<strlen(cdet.call_dest_alias)<<")");
		WRAPTRACE(3, "Call source e164: "<< cdet.call_source_e164<<"("<<strlen(cdet.call_source_e164)<<")");
		WRAPTRACE(3, "Call dest e164: "<< cdet.call_dest_e164<<"("<<strlen(cdet.call_dest_e164)<<")");
		WRAPTRACE(3, "Call RDNIS: "<< cdet.call_rdnis<<"("<<strlen(cdet.call_rdnis)<<")");
		WRAPTRACE(3, "Remote Party number: "<<GetRemotePartyNumber());
		WRAPTRACE(3, "Remote Party name: "<<GetRemotePartyName());
		WRAPTRACE(3, "Remote Party address: "<<GetRemotePartyAddress());
		WRAPTRACE(3, "Remote Application: "<< cdet.remote_app<<"("<<strlen(cdet.remote_app)<<")");
		// DEBUG END

		// Invoke the application callback
		if (on_connection_init != NULL)
			res = on_connection_init(cdet);
		else {
			cout << "H.323 WARNING: No call initiation handling!" << endl;
			res = -1;
		}
		Unlock();
	} else {
		WRAPTRACE(1, "Failed to lock connection.");
		return H323Connection::AnswerCallDenied;
	}

	if (res < 0) {
		WRAPTRACE(2, "Failed to initialize incoming H.323 call. Dropping it.");
		return H323Connection::AnswerCallDenied;
	}

#if 0
	if (ConnectionFastStart) {
		// Send CALL_PROCEEDING with media open.
		return H323Connection::AnswerCallDeferredWithMedia;
	} else {
		return H323Connection::AnswerCallPending;
	}
	//return H323Connection::AnswerCallDeferred;
#endif
	return H323Connection::AnswerCallDeferredWithMedia;
}

/*****************************************************************************
 *
 */
BOOL 
WrapH323Connection::OnAlerting(const H323SignalPDU & alertingPDU,
		const PString & username)
{
	call_details_t cdet;
	unsigned progress;

	WRAPTRACE(2, "Ringing phone for \"" << username << "\" ...");

	if (Lock()) {
		// Invoke the application callback
		cdet.app_id = GetAppID();
		cdet.call_reference = GetCallReference();
		strncpy(cdet.call_token, (const char*)GetCallToken(), 
						sizeof(cdet.call_token) - 1);
		if (alertingPDU.GetQ931().GetProgressIndicator(progress) == FALSE)
			progress = 0;
		if (on_h323_exception) {
			if ((progress == Q931::ProgressNotEndToEndISDN) || 
				(progress == Q931::ProgressInbandInformationAvailable)) {
				on_h323_exception(cdet, OH323EXC_CALL_PROGRESS, NULL);
				on_h323_exception(cdet, OH323EXC_CALL_ALERTED, NULL);
			} else {
				on_h323_exception(cdet, OH323EXC_CALL_ALERTED, NULL);
			}
		} else {
			cout << "H.323 WARNING: No exception (alerting) handling!" << endl;
			Unlock();
			return FALSE;
		}
		Unlock();
	} else {
		WRAPTRACE(1, "Failed to lock connection.");
		return FALSE;
	}

	return TRUE;
}

/*****************************************************************************
 *
 */
BOOL 
WrapH323Connection::OnReceivedProgress(const H323SignalPDU & progressPDU)
{
	call_details_t cdet;
	unsigned progress;

	WRAPTRACE(2, "Received PROGRESS message...");
	if (Lock()) {
		// Invoke the application callback
		cdet.app_id = GetAppID();
		cdet.call_reference = GetCallReference();
		strncpy(cdet.call_token, (const char*)GetCallToken(), 
						sizeof(cdet.call_token) - 1);
		if (progressPDU.GetQ931().GetProgressIndicator(progress) == FALSE)
			progress = 0;
		if (on_h323_exception) {
			if ((progress == Q931::ProgressNotEndToEndISDN) || 
				(progress == Q931::ProgressInbandInformationAvailable)) {
				on_h323_exception(cdet, OH323EXC_CALL_PROGRESS, NULL);
			}
		} else {
			cout << "H.323 WARNING: No exception (progress) handling!" << endl;
			Unlock();
			return FALSE;
		}
		Unlock();
	} else {
		WRAPTRACE(1, "Failed to lock connection.");
		return FALSE;
	}

	return(H323Connection::OnReceivedProgress(progressPDU));
}

/*****************************************************************************
 *
 */
BOOL 
WrapH323Connection::OnReceivedSignalSetup(const H323SignalPDU & setupPDU)
{
	// Upon receiption of a SETUP message, we must respond soon
	// enough, because the remote endpoint has fired a 4-second (or greater)
	// timer waiting for an initial reply (not necessarily a CONNECT).
	// After the receiption of this initial reply, the remote endpoint
	// will wait additionally another 180 seconds (at least) for a
	// final reply (like CONNECT or RELEASE COMPLETE).
	WRAPTRACE(2, "Received SETUP message...");

	sourceAliases = setupPDU.GetSourceAliases();
	destAliases = setupPDU.GetDestinationAlias();
	sourceE164 = "";
	setupPDU.GetSourceE164(sourceE164);
	destE164 = "";
	setupPDU.GetDestinationE164(destE164);

	return H323Connection::OnReceivedSignalSetup(setupPDU);
}

/*****************************************************************************
 *
 */
BOOL 
WrapH323Connection::OnSendSignalSetup(H323SignalPDU & setupPDU)
{
	WRAPTRACE(2, "Sending SETUP message...");

	if (localAliasNames.GetSize() > 0) {
		WRAPTRACE(3, "Setting display name " << localAliasNames[0]);
		setupPDU.GetQ931().SetDisplayName(localAliasNames[0]);
		if (localAliasNames.GetSize() > 1) {
			WRAPTRACE(3, "Setting calling party number " << localAliasNames[1]);
			setupPDU.GetQ931().SetCallingPartyNumber(localAliasNames[1]);
		}
	}

	/* XXX Set 'Called Party Number' from DNID field */

	sourceAliases = setupPDU.GetSourceAliases();
	destAliases = setupPDU.GetDestinationAlias();
	sourceE164 = "";
	setupPDU.GetSourceE164(sourceE164);
	destE164 = "";
	setupPDU.GetDestinationE164(destE164);

	return H323Connection::OnSendSignalSetup(setupPDU);
}

/*****************************************************************************
 *
 */
void
WrapH323Connection::OnReceivedReleaseComplete(const H323SignalPDU & pdu)
{
	WRAPTRACE(2, "Received RELEASE COMPLETE message [" << GetCallToken() << "]");
	return H323Connection::OnReceivedReleaseComplete(pdu);
}

/*****************************************************************************
 *
 */
BOOL
WrapH323Connection::OnSendReleaseComplete(H323SignalPDU & pdu)
{
	WRAPTRACE(2, "Sending RELEASE COMPLETE message [" << GetCallToken() << "]");
	if (q931_cause != Q931::ErrorInCauseIE)
		pdu.GetQ931().SetCause(q931_cause);
	return H323Connection::OnSendReleaseComplete(pdu);
}

/*****************************************************************************
 *
 */
void
WrapH323Connection::OnEstablished()
{
	WRAPTRACE(3, "WrapH323Connection [" << GetCallToken() << "] established ("
		<< fastStartState << "/" 
		<< (h245Tunneling ? "H245Tunneling":"noH245Tunneling") << ")");

	H323Connection::OnEstablished();
}

/*****************************************************************************
 *
 */
BOOL
WrapH323Connection::OnClosingLogicalChannel(H323Channel & channel)
{
	WRAPTRACE(2, "Closing logical channel [" << GetCallToken() << "]");
//	if (channel.GetCodec() != NULL) {
//		(channel.GetCodec())->CloseRawDataChannel();
//	}    

	return H323Connection::OnClosingLogicalChannel(channel);
}

/*****************************************************************************
 *
 */
BOOL
WrapH323Connection::OnReceivedFacility(const H323SignalPDU & pdu)
{
	WRAPTRACE(2, "Received FACILITY message [" << GetCallToken() << "]");
	return H323Connection::OnReceivedFacility(pdu);
}

/*****************************************************************************
 *
 */
BOOL
WrapH323Connection::OnControlProtocolError(ControlProtocolErrors errorSource,
				const void *errorData)
{
	int res;
	call_details_t cdet;
	char msg_buf[512];
	char msg_buf1[512];

	cout << "*** [" << GetCallToken() << "] H.323 CONTROL PROTOCOL ERROR " << endl;

	memset(msg_buf, 0, sizeof(msg_buf));
	memset(msg_buf1, 0, sizeof(msg_buf1));
	switch (errorSource) {
		case H323Connection::e_MasterSlaveDetermination:
			snprintf(msg_buf1, sizeof(msg_buf1) - 1, "Master-Slave Determination");
			break;
		case H323Connection::e_CapabilityExchange:
			snprintf(msg_buf1, sizeof(msg_buf1) - 1, "Capability Exchange");
			break;
		case H323Connection::e_LogicalChannel:
			snprintf(msg_buf1, sizeof(msg_buf1) - 1, "Logical Channel");
			break;
		case H323Connection::e_ModeRequest:
			snprintf(msg_buf1, sizeof(msg_buf1) - 1, "Mode Request");
			break;
		case H323Connection::e_RoundTripDelay:
			snprintf(msg_buf1, sizeof(msg_buf1) - 1, "Roundtrip Delay");
			break;
		default:
			snprintf(msg_buf1, sizeof(msg_buf1) - 1, "Unknown");
			break;
	}
	if (errorData)
		snprintf(msg_buf, sizeof(msg_buf) - 1, "%s [%s]", msg_buf1, (char *)errorData);
	else
		snprintf(msg_buf, sizeof(msg_buf) - 1, "%s", msg_buf1);

	// Special case for round trip delay errors
	if ((errorSource == H323Connection::e_RoundTripDelay) &&
		(endpoint.ShouldClearCallOnRoundTripFail() == FALSE)) 
		return TRUE;

	// Invoke the application callback
	if (on_h323_exception) {
		// Fill the structure
		cdet.app_id = GetAppID();
		cdet.call_reference = GetCallReference();
		strncpy(cdet.call_token, (const char*)GetCallToken(), 
					sizeof(cdet.call_token) - 1);
		res = on_h323_exception(cdet, OH323EXC_CTRL_ERROR, msg_buf);
	} else {
		cout << "H.323 WARNING: No exception handling!" << endl;
		res = -1;
	}

	if (res < 0)
		return FALSE;
	return TRUE;
}

// End of file //////////////////////////////////////////////////////////////
